`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    18:07:44 05/28/2013 
// Design Name: 
// Module Name:    BoardConfiguration 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
/* This configuration:
    MMIO0: LEDs
    MMIO1: Switches
    MMIO2: Seven Seg Hex
    MMIO3: Hex Keypad
*/
module BoardConfiguration(
	input clk,
	input clk_50,
	input EppAstb,
	input EppDstb,
	input EppWR,
	output EppWait,
	inout [7:0] EppDB,
	output [7:0] leds,
	input [7:0] switches,
	input [4:0] pButtons,
	output [6:0] segDisplaySegs,
	output segDisplayDot,
	output [3:0] segDisplaySelector,
	inout PS21C,
	inout PS21D,
	inout PS22C,
	inout PS22D,
	output [2:0] vgaRed,
	output [2:0] vgaGreen,
	output [2:1] vgaBlue,
	output Hsync,
	output Vsync,
	inout [7:0] JA,
	inout [7:0] JB,
    inout [7:0] JC,
	inout [7:0] JD
    );
	
	//clock wires
	wire mclk = clk;
	wire clk_25;
	wire clk_12p5;
	
	ClockDivider #(2) clkDiv(clk_50, {clk_12p5, clk_25});
	
	//reset wires
	wire reset = pButtons[0];
	wire CPU_Master_Reset;
	    
	//instatiation of Board Interface
	wire [1:0] mode;
	wire [0:0] debug_cpu_clks;
	wire [3:0] memorySelector;
	wire [31:0] BI_MemorySize;
	wire [31:0] BI_MEM_Address;
	wire [31:0] BI_Mem_Out;
	wire BI_Mem_Done;
	wire [31:0] BI_Mem_DataIn;
	wire BI_MEM_Write;
	wire [3:0] cpuSelector;
	wire [7:0] cpuFrequency;
	wire [31:0] cpuPC;
	wire [4:0] cpuRegisterSelector;
	wire [31:0] cpuRegisterValue;

	BoardInterface #(1, 1) BoardInterfaceInstance(mclk, clk_50, reset,
	mode, pButtons[1],
	debug_cpu_clks,
	EppAstb, EppDstb, EppWR, EppWait, EppDB, //EPP wires
	memorySelector, BI_MemorySize, BI_MEM_Address, BI_Mem_Out, BI_Mem_Done, BI_Mem_DataIn, BI_MEM_Write,
	cpuSelector, cpuFrequency, cpuPC, cpuRegisterSelector, cpuRegisterValue);
	
	
	//instatiation of SevenSeg Hardware Basic
	reg [3:0] sevenSegEnables;
	reg [15:0] sevenSegData;
	reg [3:0] sevenSegDots;
	SevenSeg debuggerSeg(clk_50,
	 sevenSegEnables,
    sevenSegData[3:0],
    sevenSegData[7:4],
    sevenSegData[11:8],
	 sevenSegData[15:12],
	 sevenSegDots,
	 segDisplaySegs,
	 segDisplayDot,
	 segDisplaySelector
    );
	 
	//other wire defs
	parameter MODE_PC_DOWNLOADER = 2'b00;
	parameter MODE_UNKNOWN = 2'b01;
	parameter MODE_DEBUG = 2'b10;
	parameter MODE_RUN = 2'b11;
	
	assign CPU_Master_Reset = (mode == MODE_PC_DOWNLOADER) ? 1'b1 : 1'b0;
	
	wire clk_cpu0 = clk_25;
	wire CPU_0_Reset = pButtons[0];
	
	//MEM_HEADER_WIRES
	
	/////// MEMORY 1 ////////
	
	wire [31:0] MEM1_Address1;
	wire [31:0] MEM1_DataOut1;
	wire [31:0] MEM1_DataIn1;
	wire MEM1_Write1;
	wire MEM1_Ack1 = 1;
	wire [31:0] MEM1_Address2;
	wire [31:0] MEM1_DataOut2;
	wire [31:0] MEM1_DataIn2;
	wire MEM1_Write2 = 0;
	wire MEM1_Ack2 = 1;
	wire MEM1_Address1_Invalid = |MEM1_Address1[31:10];
	wire MEM1_Address2_Invalid = |MEM1_Address2[31:10];
	
	DISMEM32w256d simpleMemory (
  MEM1_Address1[9:2], // input [7 : 0] a
  MEM1_DataIn1, // input [31 : 0] d
  MEM1_Address2[9:2], // input [7 : 0] dpra
  mclk, // input clk
  MEM1_Write1, // input we
  MEM1_DataOut1, // output [31 : 0] spo
  MEM1_DataOut2 // output [31 : 0] dpo
);

	//this is a memory output selector for the Board Interface Module
	reg BI_Mem_DoneReg;
	reg [31:0] BI_Mem_OutReg;
	assign BI_Mem_Done = BI_Mem_DoneReg;
	assign BI_Mem_Out = BI_Mem_OutReg;
	always @(*) begin
		case(memorySelector)
			4'd0: begin
				BI_Mem_OutReg = MEM1_DataOut1;
				BI_Mem_DoneReg = MEM1_Ack1;
			end
			default: begin
				BI_Mem_OutReg = 32'b0;
				BI_Mem_DoneReg = 1;
			end
		endcase
	 end
	
	//SYSTEM 1 Bus Wire Definitions
	wire [31:0] SYS1_InstructionBus_Address;
	wire [31:0] SYS1_InstructionBus_Data;
	wire SYS1_InstructionBus_InvalidAddress = MEM1_Address2_Invalid;
	wire SYS1_InstructionBus_Ack;
	wire [31:0] SYS1_DataBus_Address;
	wire [31:0] SYS1_DataBus_DataFromCPU;
	wire [31:0] SYS1_DataBus_DataToCPU;
	wire SYS1_DataBus_InvalidAddress;
	wire SYS1_DataBus_Write;
	wire SYS1_DataBus_Ack;
	
	wire SYS1_DataBus_MMIO = &SYS1_DataBus_Address[31:16];
	
	wire cpu0_clk;
	wire cpu0_reset;
	
	wire SYS1_DataBus_Read;
	
	// mmio controllers
	
	localparam n_mmio = 4; // change this if you need more mmio peripherals.
	
	wire [15:0] mmio_bus_enable;
	wire [15:0] mmio_bus_interrupt;
	wire [31:0] mmio_bus_dataOut [15:0];
	wire [15:0] mmio_bus_done;
	
	// setting unused peripherals dataOuts to 0, and dones to 1, interrupts to 0.
	genvar i;
	generate
	for(i=n_mmio;i<16;i=i+1)
	begin:set_unused_mmio
		assign mmio_bus_dataOut[i] = 32'b0;
		assign mmio_bus_done[i] = 1;
		assign mmio_bus_interrupt[i] = 0;
	end
	endgenerate
	
	//mmio controller Definitions
		// look through the source files for ideas on writing your own.
		
	//On-Board Led Controller
	
	OnBoardLedController OBLC_Instance(
			mclk,
			reset,
			mmio_bus_enable[0],
			SYS1_DataBus_Address[5:2],
			SYS1_DataBus_DataFromCPU,
			mmio_bus_dataOut[0],
			SYS1_DataBus_Write,
			mmio_bus_done[0],
			mmio_bus_interrupt[0],
			leds
		);
	 
	//On-Board Switches Controller
	
	OnBoardSwitchesController OBSWC_Instance(
		mclk,
		reset,
		mmio_bus_enable[1],
		SYS1_DataBus_Address[5:2],
		SYS1_DataBus_DataFromCPU,
		mmio_bus_dataOut[1],
		SYS1_DataBus_Write,
		mmio_bus_done[1],
		mmio_bus_interrupt[1],
		switches
	);
	
	//On-Board 7 Seg Hex Controller
	
	// note: this peripheral takes extra wires for usage with debugging tools & mode display
	// via the dots on the seven segs.
	wire [3:0] OBSSC_digitEnables;
	wire [15:0] OBSSC_data;
	wire [3:0] OBSSC_dots;
	
	OnBoard7SegHexController OBSS_Instance(
			mclk,
			reset,
			mmio_bus_enable[2],
			SYS1_DataBus_Address[5:2],
			SYS1_DataBus_DataFromCPU,
			mmio_bus_dataOut[2],
			SYS1_DataBus_Write,
			mmio_bus_done[2],
			mmio_bus_interrupt[2],
			OBSSC_digitEnables,
			{OBSSC_dots, OBSSC_data}
		);
	
	// write your own
	// and instantiate them here.
	// uncomment these, and change the instance names and module names as appropriate.
	// add wires to the definition if your peripheral needs a precise clock etc.
	
	HexKeypadController HexKeypad(
			cpu0_clk,
			reset,
			mmio_bus_enable[3],
			SYS1_DataBus_Address[5:2],
			SYS1_DataBus_DataFromCPU,
			mmio_bus_dataOut[3],
			SYS1_DataBus_Write,
			mmio_bus_done[3],
			mmio_bus_interrupt[3],
            JC
		);
	/*mmioPeripheral4 mmioInstance4(
			mclk,
			reset,
			mmio_bus_enable[4],
			SYS1_DataBus_Address[5:2],
			SYS1_DataBus_DataFromCPU,
			mmio_bus_dataOut[4],
			SYS1_DataBus_Write,
			mmio_bus_done[4],
			mmio_bus_interrupt[4]
		);*/
		
		// ...
		
		// 16 peripherals available. (indexable from 0 to 15.)
		
		// ...
			
	/*mmioPeripheral15 mmioInstance15(
			mclk,
			reset,
			mmio_bus_enable[15],
			SYS1_DataBus_Address[5:2],
			SYS1_DataBus_DataFromCPU,
			mmio_bus_dataOut[15],
			SYS1_DataBus_Write,
			mmio_bus_done[15],
			mmio_bus_interrupt[15]
		);*/
			
			
	
	///***************** SYSTEM DEFINITIONS ******************* ///
	
	/// ###################### SYS1 ########################### ///
	
	
	//START SYSTEM 1 LOGIC
	reg [31:0] MEM1_Address1Reg;
	reg [31:0] MEM1_DataIn1Reg;
	reg MEM1_Write1Reg;
	reg cpu0_clkReg;
	assign MEM1_Address1 = MEM1_Address1Reg;
	assign MEM1_DataIn1 = MEM1_DataIn1Reg;
	assign MEM1_Write1 = MEM1_Write1Reg;
	assign cpu0_clk = cpu0_clkReg;
	
	always @(*) begin
		case (mode)
			MODE_PC_DOWNLOADER: begin
				MEM1_Address1Reg = (memorySelector == 4'b0) ? BI_MEM_Address : 32'b0;
				MEM1_DataIn1Reg = (memorySelector == 4'b0) ? BI_Mem_DataIn : 32'b0;
				MEM1_Write1Reg = (memorySelector == 4'b0) ? BI_MEM_Write : 1'b0;
				cpu0_clkReg = 0;
			end
			MODE_UNKNOWN: begin
				MEM1_Address1Reg = {22'b0, switches, 2'b0};
				MEM1_DataIn1Reg = 32'b0;
				MEM1_Write1Reg = 0;
				cpu0_clkReg = 0;
			end
			MODE_DEBUG: begin
				MEM1_Address1Reg = (SYS1_DataBus_MMIO ? 32'b0 : SYS1_DataBus_Address);
				MEM1_DataIn1Reg = SYS1_DataBus_DataFromCPU;
				MEM1_Write1Reg = ~SYS1_DataBus_MMIO & SYS1_DataBus_Write;
				cpu0_clkReg = debug_cpu_clks[0];
			end
			MODE_RUN: begin
				MEM1_Address1Reg = (SYS1_DataBus_MMIO ? 32'b0 : SYS1_DataBus_Address);
				MEM1_DataIn1Reg = SYS1_DataBus_DataFromCPU;
				MEM1_Write1Reg = ~SYS1_DataBus_MMIO & SYS1_DataBus_Write;
				cpu0_clkReg = clk_25; //setting the default run speed of cpu 0 to 25mhz
			end
		endcase
	end
	
	assign MEM1_Address2 = SYS1_InstructionBus_Address;
	assign SYS1_InstructionBus_Data = MEM1_DataOut2;
	assign SYS1_InstructionBus_Ack = MEM1_Ack2;
	
	mips cpu_instance(
		cpu0_clk,
		CPU_0_Reset | CPU_Master_Reset,
		SYS1_InstructionBus_Address,
		SYS1_InstructionBus_Data,
		SYS1_InstructionBus_Ack,
		SYS1_InstructionBus_InvalidAddress, // InstrInvalidAddressF
		SYS1_DataBus_Write,
		SYS1_DataBus_Read,
		SYS1_DataBus_Ack,
		SYS1_DataBus_InvalidAddress, // DataInvalidAddressM
		SYS1_DataBus_Address,
		SYS1_DataBus_DataFromCPU,
		SYS1_DataBus_DataToCPU,
		mmio_bus_interrupt,
		cpuRegisterSelector,
		cpuRegisterValue
	);
	
	IRQDecoder SYS1EnableMux(SYS1_DataBus_MMIO, SYS1_DataBus_Address[9:6], mmio_bus_enable);
	
	//Controller & memory to data bus selector
	
	assign SYS1_DataBus_DataToCPU =
			SYS1_DataBus_MMIO ?	mmio_bus_dataOut[SYS1_DataBus_Address[9:6]] : MEM1_DataOut1;
	assign SYS1_DataBus_Ack =
			SYS1_DataBus_MMIO ?	mmio_bus_done[SYS1_DataBus_Address[9:6]] : MEM1_Ack1;
	assign SYS1_DataBus_InvalidAddress =
			SYS1_DataBus_MMIO ? 0 : MEM1_Address2_Invalid;
				// MMIO data is always valid
	
	//END SYSTEM 1 Logic
	
	parameter CON_CPU_NUM = 1;
	parameter CON_MEM_NUM = 1;
	 
	parameter CPU_NUM = 4'd1;
	parameter MEM_NUM = 4'd1;
	 
	 // CPU FREQUENCY CONSTANTS ROM
	 
	 reg [7:0] cpu_frequency;
	 reg [31:0] cpu_PCReg;
	 assign cpuFrequency = cpu_frequency;
	 assign cpuPC = cpu_PCReg;
	 always @(*) begin
		case(cpuSelector)
			4'd0: begin
				cpu_frequency = 8'd1;
				cpu_PCReg = SYS1_InstructionBus_Address;
				end				
			default: begin
				cpu_frequency = 8'd0;
				cpu_PCReg = 32'b0;
				end
		endcase
	 end
	
	// Memory Size CONSTANTS ROM
	 
	 reg [31:0] mem_size;
	 assign BI_MemorySize = mem_size;
	 always @(*) begin
		case(memorySelector)
			4'd0: mem_size = 32'd256;
			default: mem_size = 32'd0;
		endcase
	 end
	 
	//7Seg switcher for debugger
	always @(*) begin
		if(mode[1]) begin// if in debugger or run mode
			sevenSegEnables = OBSSC_digitEnables;
			sevenSegData = OBSSC_data;
			sevenSegDots[3:2] = OBSSC_dots[3:2];
         sevenSegDots[1:0] = mode;
		end
		else begin
			sevenSegEnables = ~(mode[0]) ? 4'd0 : 4'b1111;
			sevenSegData = (~pButtons[3] ? MEM1_DataOut1[15:0] : MEM1_DataOut1[31:16]);
			sevenSegDots[3:2] = 2'b0;
			sevenSegDots[1:0] = mode;
		end
	end
endmodule
